﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Timers;
using System.IO;
using VideoLink.Frames;

namespace VideoLink.Streams
{
    /// <summary>
    /// Представляет источник видео, воспроизводящий видео путём последовательного
    /// вывода на экран кадров, расположенных по указанному пути.
    /// Кадры могут быть сохранены в любом формате - jpg, gif, bmp, png.
    /// </summary>
    public class ByFrameJPEGSource : VideoSourceBase
    {
        #region Properties and fields

        /// <summary>
        /// Номер текущего кадра. Нумерация идёт с 0
        /// </summary>
        private int curFrame = 0;

        /// <summary>
        /// Таймер, по которому происходит подгрузка следующего кадра
        /// и вывод его на экран
        /// </summary>
        private System.Windows.Forms.Timer updateTimer;

        /// <summary>
        /// Поле, требуемое для работы дизайнера
        /// </summary>
        private System.ComponentModel.IContainer components;

        /// <summary>
        /// Контрол, на котором происходит рисование видео
        /// </summary>
        protected Canvas canvas;

        /// <summary>
        /// Шаблон пути, по которому находятся файлы кадров.
        /// Данный шаблон должен содержить одно подстановочное поле,
        /// в которое будет подставляться номер кадра (нумерация - с нуля).
        /// Формат подстановочного поля определяется .Net (String.Format())
        /// </summary>
        public string FileNameTemplate { get; set; }
        
        #endregion

        /// <summary>
        /// Создать экземпляр класса по умолчанию
        /// </summary>
        public ByFrameJPEGSource()
            : base()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Создать экземпляр класса с использованием указанных параметров
        /// </summary>
        /// <param name="streamID">ID потока</param>
        /// <param name="fileNameTemplate">Шаблон пути до файлов</param>
        /// <param name="fps">Количество кадров в секунду</param>
        public ByFrameJPEGSource(int streamID, string fileNameTemplate, float fps)
            : base(streamID)
        {
            InitializeComponent();
            FileNameTemplate = fileNameTemplate;
            updateTimer.Interval = (int)(1000 / fps);
        }

        #region Designer

        /// <summary>
        /// Инициализировать компонент. Редактируется дизайнером
        /// </summary>
        private void InitializeComponent()
        {
            this.components = new System.ComponentModel.Container();
            this.updateTimer = new System.Windows.Forms.Timer(this.components);
            this.canvas = new Canvas();
            this.SuspendLayout();
            // 
            // updateTimer
            // 
            this.updateTimer.Interval = 40;
            this.updateTimer.Tick += new System.EventHandler(this.updateTimer_Tick);
            //
            // canvas
            //
            this.ViewportPanel.Controls.Add(canvas);
            this.canvas.Dock = System.Windows.Forms.DockStyle.Fill;
            // 
            // ByFrameJPEGSource
            // 
            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
            this.Name = "ByFrameJPEGSource";
            this.ResumeLayout(false);

        }
        
        #endregion

        #region Playback

        /// <summary>
        /// Получить данные, соответствующие данному кадру.
        /// </summary>
        /// <returns>
        /// Текущее состояние потока
        /// Если воспроизведение в данный момент идёт или State только что изменилось, 
        /// то используется OneFrame, иначе возвращается null
        /// </returns>
        public override VideoStreamState GetFrameData()
        {
            VideoStreamState result = null;
            if ((State == PlaybackState.Play) || (needSendData))
            {
                result = new VideoStreamState(StreamID,
                    State,
                    new OneFrame(canvas.ImageToDraw));

                //если состояние не play, то надо послать состояние
                //один раз и успокоиться
                needSendData = State == PlaybackState.Play;
            }

            return result;
        }

        /// <summary>
        /// Вызывается при изменении свойства State в значение Play
        /// или при нажатии кнопки "Play" на контроле.
        /// Выполняются все действия, связанные с запуском воспроизведения
        /// </summary>
        /// <param name="sender">-</param>
        /// <param name="e">-</param>
        protected override void OnPlay(object sender, EventArgs e)
        {
            base.OnPlay(sender, e);
            updateTimer.Enabled = true;
        }

        /// <summary>
        /// Вызывается при изменении свойства State в значение Play
        /// или при нажатии кнопки "Play" на контроле.
        /// Выполняются все действия, связанные с приостановкой воспроизведения
        /// </summary>
        /// <param name="sender">-</param>
        /// <param name="e">-</param>
        protected override void OnPause(object sender, EventArgs e)
        {
            base.OnPause(sender, e);
            updateTimer.Enabled = false;
        }

        /// <summary>
        /// Вызывается при изменении свойства State в значение Play
        /// или при нажатии кнопки "Play" на контроле.
        /// Выполняются все действия, связанные с остановкой воспроизведения
        /// </summary>
        /// <param name="sender">-</param>
        /// <param name="e">-</param>
        protected override void OnStop(object sender, EventArgs e)
        {
            base.OnStop(sender, e);
            updateTimer.Enabled = false;
            canvas.ImageToDraw = null;
            curFrame = 0;
        }

        /// <summary>
        /// Обработчик тика таймера. 
        /// Тут происходит подгрузка следующего кадра с жесткого и
        /// обновление изображения на экране.
        /// Если следующий кадр не найден или шаблон пути имеет неправильный формат,
        /// то воспроизведение останавливается
        /// </summary>
        /// <param name="sender">-</param>
        /// <param name="e">-</param>
        private void updateTimer_Tick(object sender, EventArgs e)
        {
            try
            {
                var nextFile = String.Format(FileNameTemplate, curFrame);

                if (!File.Exists(nextFile))
                {
                    State = PlaybackState.Stop;
                    return;
                }

                canvas.ImageToDraw = Bitmap.FromFile(nextFile) as Bitmap;
                curFrame++;
            }
            catch (FormatException)
            {
                State = PlaybackState.Stop;
            }
        }

        #endregion
    }
}
